import importlib.util
import inspect
import json
import os.path

from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt

from noflip_web.globals import engine_ins_dc
from tools.methods import send_data_to_front, save_global_send_data, update_active_model, gen_new_variable, load_var_json
from tools.methods_2 import gen_data_info
from tools.operate import read_dc_csv, read_csv, delete_model, get_model_name_code
from tools.function import gen_new_engine
from noflip_web.settings import BASE_DIR
from operate.models import MkData, SgTactic
from datetime import datetime as datetime_


def lab(request):
    model_name = request.GET.get('model_name', None)
    # 获取model_name_code

    if model_name is None:
        # 根据全局变量是否赋值决定是否从json加载数据
        if engine_ins_dc["active_engine"]:
            pass
        else:
            load_var_json()

            model_name_code = engine_ins_dc["active_engine"]
            # 生成相应engine_ins
            engine_ins = gen_new_engine(model_name_code)
            # 更新全局变量中激活相关变量
            update_active_model(engine_ins)
    else:
        model_name_code = get_model_name_code(model_name)
        # 全局配置指定激活的engine
        if engine_ins_dc["active_engine"] == model_name_code:
            # 找出engine_ins
            engine_ins = engine_ins_dc["single_engine_dc"][model_name_code]
            # 更新全局变量中激活相关变量
            update_active_model(engine_ins)
            pass
        else:
            # 如果不是当前激活的，且engine_ins已经存在
            if model_name_code in engine_ins_dc["single_engine_dc"]:
                # 找出engine_ins
                engine_ins = engine_ins_dc["single_engine_dc"][model_name_code]
                # 更新全局变量中激活相关变量
                update_active_model(engine_ins)
            # 如果不是当前激活的，且engine_ins不存在
            else:
                # 如果active_engine从未被指定
                # 生成相应engine_ins
                engine_ins = gen_new_engine(model_name_code)
                # 更新全局变量中激活相关变量
                update_active_model(engine_ins)

    return render(request, 'lab.html')  # 渲染实验室页面


def update_data(request):
    """前端发起更新数据的请求，不需跳转界面"""
    # 可用于页面加载后的数据初始化以及对接前端定时获取变量的请求（比如实现实时更新回测进度图像的需求）
    # 从变量json文件中读取要在lab的part2中显示的变量数据
    now_model_code = engine_ins_dc["active_engine"]
    variable_data = engine_ins_dc["all_variable"][now_model_code]
    model_list = []
    for k, v in engine_ins_dc["all_variable"].items():
        model_list.append([v["lab_var_part2_1"]["title"], v["lab_var_part2_1"]["sub_title"]])
    send_data = {'model_data': variable_data, "model_list": model_list}

    # 发送

    return send_data_to_front(send_data)


def click_model(request):
    """点击了模型"""
    model_name = request.GET.get("model_name")
    # 根据model_name获取model_name_code
    model_name_code = get_model_name_code(model_name)

    # 初始化或从全局中加载当前引擎
    if model_name_code in engine_ins_dc:
        engine_ins = engine_ins_dc["single_engine_dc"][model_name_code]
        pass
    else:
        # 生成引擎实例
        engine_ins = gen_new_engine(model_name_code)
    # 更新全局变量中激活相关变量
    update_active_model(engine_ins)
    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][model_name_code]
    }
    return save_global_send_data(context)
    # 跳转lab页，嵌入operate的introduce页


@csrf_exempt
def del_model(request):
    """删除了模型"""
    if request.method == 'POST':
        data = json.loads(request.body)
    else:
        raise ValueError("非post")
    model_name = data.get("title")
    new_model_name_code = delete_model(model_name)
    # 找到model引擎
    if new_model_name_code in engine_ins_dc["single_engine_dc"]:
        engine_ins = engine_ins_dc["single_engine_dc"][new_model_name_code]
    else:
        # 新建一个engine_ins
        # 初始化引擎实例
        engine_ins = gen_new_engine(new_model_name_code)
    update_active_model(engine_ins)
    # 生成模型列表
    model_list = []
    for k, v in engine_ins_dc["all_variable"].items():
        model_list.append([v["lab_var_part2_1"]["title"], v["lab_var_part2_1"]["sub_title"]])
    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][new_model_name_code],
        "model_list": model_list
    }

    return save_global_send_data(context)


@csrf_exempt
def modify_model(request):
    """修改了model"""
    if request.method == 'POST':
        data = json.loads(request.body)
    else:
        raise ValueError("非post")
    old_title = data.get("old_title")
    new_title = data.get("title")
    new_sub_title = data.get("sub_title")

    # 计算model_code
    model_code = get_model_name_code(old_title)
    # 判断新title是否可用
    from tools.methods import judge_title
    if judge_title(new_title, engine_ins_dc["all_variable"], old_title):
        pass
    else:
        raise ValueError("新的模型名不可用")

    # 更改时保持key为最初的值，只是更新其下title和sub_title两个字段
    found_num = 0
    for k, v in engine_ins_dc["all_variable"].items():
        if v["lab_var_part2_1"]["title"] == old_title:
            found_num += 1
    if found_num == 0:
        raise ValueError(f"未找到指定的old_title：{old_title}，逻辑错误")
    elif found_num > 1:
        raise ValueError(f"找到{found_num}个指定的old_title，逻辑错误")
    else:
        # 生成模型列表
        model_list = []
        for k, v in engine_ins_dc["all_variable"].items():
            if v["lab_var_part2_1"]["title"] == old_title:
                v["lab_var_part2_1"]["title"] = new_title
                v["lab_var_part2_1"]["sub_title"] = new_sub_title
            model_list.append([v["lab_var_part2_1"]["title"], v["lab_var_part2_1"]["sub_title"]])

    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][model_code],
        "model_list": model_list
    }

    return save_global_send_data(context)


def only_refresh_data():
    # 获取data/single下的所有行情数据，并将其信息存入数据库（完全删除数据表内容再重新生成）
    data_dir = os.path.join(BASE_DIR, 'mk_data', 'single')
    mk_data_ls = []
    for f in os.listdir(data_dir):
        if f.endswith('.csv') and os.path.isfile(os.path.join(data_dir, f)):
            data_path = os.path.join(data_dir, f)

            data_info_dc = gen_data_info(data_path)

            mk_data = MkData(name=data_info_dc["name"],
                             interval=data_info_dc["interval"],
                             data_modified_time=data_info_dc["data_modified_time"],
                             start_time=data_info_dc["start_time"],
                             end_time=data_info_dc["end_time"],
                             number=data_info_dc["number"],
                             symbol=data_info_dc["symbol"],)
            mk_data_ls.append(mk_data)
    # 将数据库中的数据清除
    MkData.objects.all().delete()
    # 保存刚刚生成的数据
    MkData.objects.bulk_create(mk_data_ls)
    # 获取object/single/tactic下的所有战法数据，并将其信息存入数据库（完全删除数据表内容再重新生成）
    file_path = os.path.join(BASE_DIR, "object", "single", "tactic.py")
    # 动态导入模块
    module_name = os.path.splitext(os.path.basename(file_path))[0]
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)

    # 获取所有类
    sg_tactic_ls = []
    for name, obj in inspect.getmembers(module, inspect.isclass):
        if obj.__module__ == module_name:
            # 提取类的属性并构建字典
            sg_tactic_attributes = {}
            for key, value in vars(obj()).items():
                if not key.startswith("__") and not inspect.ismethod(value):
                    if key == "tactic_type":
                        sg_tactic_attributes[key] = value.value
                    elif key == "bt_task" or key == "idx":
                        continue
                    else:
                        sg_tactic_attributes[key] = value
                else:
                    pass
            # 再添加一项战法类名
            sg_tactic_attributes['class_name'] = name

            sg_tactic_ls.append(SgTactic(**sg_tactic_attributes))
    # 将数据库中的数据清除
    SgTactic.objects.all().delete()
    # 保存刚刚生成的数据
    SgTactic.objects.bulk_create(sg_tactic_ls)


def refresh_data(request):
    """点击更新数据按钮，重新扫描data下的数据和相应tactic下的类并更新数据库"""
    only_refresh_data()
    return JsonResponse({"success": True})


@csrf_exempt
def add_model(request):
    """根据传入的参数生成新的variable和engine"""
    if request.method == 'POST':
        data = json.loads(request.body)
    else:
        raise ValueError("非post")
    title = data.get("title")
    sub_title = data.get("sub_title")
    # 生成初始variable
    model_name_code, variable_dc = gen_new_variable(title, sub_title, engine_ins_dc["all_variable"])
    if model_name_code not in engine_ins_dc["all_variable"]:
        # 加入全局变量
        engine_ins_dc["all_variable"][model_name_code] = variable_dc
    else:
        raise ValueError("model_name_code已经存在，系统生成的键有误，请重试，极小概率事件。")
    # 生成相应初始engine_ins
    engine_ins = gen_new_engine(model_name_code)
    # 将其设为活跃的引擎
    update_active_model(engine_ins)
    # 生成模型列表
    model_list = []
    for k, v in engine_ins_dc["all_variable"].items():
        model_list.append([v["lab_var_part2_1"]["title"], v["lab_var_part2_1"]["sub_title"]])

    context = {
        "model_data": engine_ins_dc["all_variable"][model_name_code],
        "model_list": model_list
    }

    return save_global_send_data(context)


def change_dir(request):
    """更改回测方向"""
    active_engine = engine_ins_dc["active_engine"]
    # 加载数据到引擎
    active_engine_ins = engine_ins_dc["single_engine_dc"][active_engine]
    active_engine_ins.variable_value["strategy_direction"] = active_engine_ins.variable_value["strategy_direction"] * -1
    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][active_engine]
    }

    return save_global_send_data(context)


def load_data(request):
    """"""
    show_data = request.GET.get("show_data")
    # 将选中的数据名计入variable
    active_engine = engine_ins_dc["active_engine"]
    engine_ins_dc["all_variable"][active_engine]["lab_var_part2_2"]["show_data"] = show_data
    # 加载数据到引擎
    active_engine_ins = engine_ins_dc["single_engine_dc"][active_engine]
    # 判断1是否点亮
    if True:
        pass
    else:
        raise ValueError("模块1未执行")
    active_engine_ins.gen_com_data()
    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][active_engine]
    }

    return save_global_send_data(context)


def add_tactic(request):
    """"""
    selected_tactic = request.GET.get("selected_tactic")
    sg_tactic_name = selected_tactic.split(" | ")[1]
    # 判断战法所属类别
    sg_tactic = SgTactic.objects.get(name=sg_tactic_name)
    active_engine = engine_ins_dc["active_engine"]
    # 将其加入相应variable
    if sg_tactic.tactic_type in ["过滤"]:
        if sg_tactic.name not in engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["filter_ls"]:
            engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["filter_ls"].append(sg_tactic.name)
    elif sg_tactic.tactic_type in ["开仓"]:
        if sg_tactic.name not in engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["open_ls"]:
            engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["open_ls"].append(sg_tactic.name)
    elif sg_tactic.tactic_type in ["平仓"]:
        if sg_tactic.name not in engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["close_ls"]:
            engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["close_ls"].append(sg_tactic.name)
    else:
        raise ValueError("不支持的战法类型")

    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][active_engine]
    }

    return save_global_send_data(context)


def remove_tactic(request):
    """根据tactic的名称从相应列表中删除，相应列表为：filter_ls open_ls close_ls"""
    remove_tactic = request.GET.get("remove_tactic")
    # 判断战法所属类别
    sg_tactic = SgTactic.objects.get(name=remove_tactic)
    active_engine = engine_ins_dc["active_engine"]
    # 将其从相应分类中删除
    if sg_tactic.tactic_type in ["过滤"]:
        if sg_tactic.name in engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["filter_ls"]:
            engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["filter_ls"].remove(sg_tactic.name)
        else:
            raise ValueError("传递的战法已不存在相应列表，逻辑错误")
    elif sg_tactic.tactic_type in ["开仓"]:
        if sg_tactic.name in engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["open_ls"]:
            engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["open_ls"].remove(sg_tactic.name)
        else:
            raise ValueError("传递的战法已不存在相应列表，逻辑错误")
    elif sg_tactic.tactic_type in ["平仓"]:
        if sg_tactic.name in engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["close_ls"]:
            engine_ins_dc["all_variable"][active_engine]["lab_var_part2_3"]["close_ls"].remove(sg_tactic.name)
        else:
            raise ValueError("传递的战法已不存在相应列表，逻辑错误")
    else:
        raise ValueError("不支持的战法类型")

    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][active_engine]
    }

    return save_global_send_data(context)


def tactic_ok(request):
    """"""
    active_engine = engine_ins_dc["active_engine"]
    engine_ins = engine_ins_dc["single_engine_dc"][active_engine]
    # 判断1 2是否点亮
    if True and engine_ins.variable_value["lab_var_part2_2"]["execute"]:
        pass
    else:
        raise ValueError("模块1或2未执行")
    engine_ins.on_set_up_tactics()
    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][active_engine]
    }

    return save_global_send_data(context)


def get_factor(request):
    """"""
    active_engine = engine_ins_dc["active_engine"]
    # 加载数据到引擎
    active_engine_ins = engine_ins_dc["single_engine_dc"][active_engine]
    # 判断1 2 3是否点亮
    if True and active_engine_ins.variable_value["lab_var_part2_2"]["execute"] and \
            active_engine_ins.variable_value["lab_var_part2_3"]["execute"]:
        pass
    else:
        raise ValueError("模块1或2或3未执行")
    active_engine_ins.extract_factor_name()  # 提取因子名
    active_engine_ins.gen_factor_com_data()  # 生成全量数据
    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][active_engine]
    }

    return save_global_send_data(context)


def start_backtest(request):
    """回测和整合数据一体"""
    import django

    django.setup()

    active_engine = engine_ins_dc["active_engine"]
    # 加载数据到引擎
    active_engine_ins = engine_ins_dc["single_engine_dc"][active_engine]
    # 判断1 2 3 4是否点亮
    if True and active_engine_ins.variable_value["lab_var_part2_2"]["execute"] and \
            active_engine_ins.variable_value["lab_var_part2_3"]["execute"] and \
            active_engine_ins.variable_value["lab_var_part2_4"]["execute"]:
        pass
    else:
        raise ValueError("模块1或2或3或4未执行")
    # 将variable值中相应部分重置为空
    active_engine_ins.init_part_5()
    # 检查数据准备是否充分
    is_pass, mess = active_engine_ins.check_condition()
    if not is_pass:
        raise ValueError(mess)
    else:
        pass
    # 生成多进程数据
    active_engine_ins.gen_process_data()
    # 启动多进程
    bt_com_data = None

    # active_engine_ins.start_process()
    bt_com_data = active_engine_ins.start_process_v2()
    if bt_com_data is None:
        raise ValueError("start_process_v2没有被正常执行")
    # 对结果进行整合
    # active_engine_ins.gen_integrate()
    active_engine_ins.gen_integrate_process(bt_com_data)

    # 生成结果数据
    active_engine_ins.gen_fruit()
    # 组合返回内容
    context = {
        "model_data": engine_ins_dc["all_variable"][active_engine]
    }

    return save_global_send_data(context)
